{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "66d19c12-346f-4f07-b591-81865253eaaf",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import numpy as np\n",
    "import matplotlib\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "\n",
    "assert torch.cuda.is_available()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7d002756-6eda-488d-be12-f96146c39b0b",
   "metadata": {},
   "outputs": [],
   "source": [
    "num_classes = 3\n",
    "gt = torch.LongTensor([[ 1, 2, 2],\n",
    "      [ 3, 1, 2],\n",
    "      [ 2, 1, 1]])\n",
    "pe = torch.LongTensor([[ 2, 1, 2],\n",
    "      [ 3, 1, 1],\n",
    "      [ 1, 1, 1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "3ef452d3-fa85-45d0-aa88-147c1a1be016",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[1, 2, 2],\n",
       "         [3, 1, 2],\n",
       "         [2, 1, 1]]),\n",
       " tensor([[2, 1, 2],\n",
       "         [3, 1, 1],\n",
       "         [1, 1, 1]]))"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gt, pe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ecb223dd-fa74-4e02-8e60-28a190c1561a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[False, False,  True],\n",
       "        [ True,  True, False],\n",
       "        [False,  True,  True]])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gt == pe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "e1f4f2b5-f771-465e-96eb-1c5bb21f3ceb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f5df0e2eef0>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.imshow(gt == pe, cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "478ffe18-78a3-4dc4-9420-9b3467deac80",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f5df0d34c18>"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(211)\n",
    "plt.imshow(gt == 1, cmap='gray')\n",
    "plt.subplot(212)\n",
    "plt.imshow(pe == 1, cmap='gray')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "aa1f1509-c905-4b9d-8889-12022ce21b33",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class 1: 0.4285714328289032\n",
      "class 2: 0.20000000298023224\n",
      "class 3: 1.0\n",
      "mIoU: 0.5428571701049805\n"
     ]
    }
   ],
   "source": [
    "iou = []\n",
    "for cls in range(1, num_classes+1):\n",
    "    cls_gt = gt == cls\n",
    "    cls_pe = pe == cls\n",
    "    intersection = cls_pe & cls_gt\n",
    "    union = cls_pe | cls_gt\n",
    "    iou_i = (intersection.sum() + 1e-9) / (union.sum() + 1e-9)\n",
    "    iou.append(iou_i)\n",
    "    print(f\"class {cls}: {iou_i}\")\n",
    "print(f\"mIoU: {np.array(iou).mean()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "7154b985-ab54-494f-8aea-ec9b9bb3cc0c",
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "def miou(gt, pe, num_classes):\n",
    "    iou = []\n",
    "    for cls in range(1, num_classes+1):\n",
    "        cls_gt = gt == cls\n",
    "        cls_pe = pe == cls\n",
    "        intersection = cls_pe & cls_gt\n",
    "        union = cls_pe | cls_gt\n",
    "        iou_i = (intersection.sum() + 1e-9) / (union.sum() + 1e-9)\n",
    "        iou.append(iou_i)\n",
    "    return numpy.array(iou).mean()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "91c69244-27de-48f5-b3ea-3e06a201b884",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 1],\n",
       "       [0, 2]])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def calc_cm(gt_image, pre_image,num_class):\n",
    "    \"\"\" This function calculates the confusion matrix. \"\"\"\n",
    "    mask = (gt_image >= 0) & (gt_image < num_class)  # ground truth中所有正确(值在[0, classe_num])的像素label的mask\n",
    "    label = num_class * gt_image[mask].astype('int') + pre_image[mask] \n",
    "    # np.bincount计算了从0到n**2-1这n**2个数中每个数出现的次数，返回值形状(n, n)\n",
    "    count = np.bincount(label, minlength=num_class**2)\n",
    "    confusion_matrix = count.reshape(num_class, num_class) #21 * 21(for pascal)\n",
    "    return confusion_matrix\n",
    "        \n",
    "calc_cm(np.array([[0, 0], [1, 1]]), np.array([[1, 0], [1, 1]]), 2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
